/*
 * Node.h
 *
 *  Created on: Feb 8, 2010
 *      Author: anton.kuhlevskyi
 */

#pragma once

#include <stdexcept>
#include <sstream>

#include "Nodes/Defs.h"
#include "Utils/Compare.h"

// base binary node
class _base_node
{
	const NodeType m_type;
	const size_t m_size;

	_base_node(const _base_node&);
	_base_node& operator=(const _base_node&);

protected:
	_base_node(const NodeType type,
			   const size_t size)
		: m_type(type)
		, m_size(size)
	{};
	~_base_node(){};

public:
	inline size_t getChildrenCount() const
	{
		return m_size;
	};
	inline NodeType getNodeType() const
	{
		return m_type;
	};
};


/*
 * Base Binary node class LifeCycle
 * Static (Get child method only)
 */
template<bool _t>
class _BaseBinaryNode
	: public _base_node
{
	const static size_t m_maxSize=2;
	_BaseBinaryNode* m_children[m_maxSize];

public:
	const static bool isStatic=_t;

protected:
	_BaseBinaryNode(const NodeType type,
					const size_t size,
					_BaseBinaryNode* lh=NULL,
					_BaseBinaryNode* rh=NULL)
			: _base_node(type, size)
	{
		m_children[0] = lh;
		m_children[1] = rh;
	};


public:
	virtual ~_BaseBinaryNode()
	{
		delete m_children[0];
		delete m_children[1];
	};

	virtual _BaseBinaryNode* clone() const=0;

	virtual void accept(NodeVisitor<_BaseBinaryNode> * pVisitor) = 0;

	virtual bool compare(_BaseBinaryNode * pNode) const
	{
		return  compareNodes(this, pNode);
	};

	/*
	 * Get Children Method
	 */
	inline _BaseBinaryNode* getChild(size_t idx) const
	{
		if (idx >= getChildrenCount())
			throw std::runtime_error("::getChild() - incorrect index");

		return m_children[idx];
	};
};


/*
 * Base Binary node class LifeCycle
 * Dynamic (Get\Set child methods)
 */
template<>
class _BaseBinaryNode<false>
	: public _base_node
{
	const static size_t m_maxSize=2;
	_BaseBinaryNode* m_children[m_maxSize];

public:
	const static bool isStatic=false;

protected:
	_BaseBinaryNode(const NodeType type,
					const size_t size,
					_BaseBinaryNode* lh=NULL,
					_BaseBinaryNode* rh=NULL)
			: _base_node(type, size)
	{
		m_children[0] = lh;
		m_children[1] = rh;
	};


public:
	virtual ~_BaseBinaryNode()
	{
		delete m_children[0];
		delete m_children[1];
	};

	virtual _BaseBinaryNode* clone() const=0;

	virtual void accept(NodeVisitor<_BaseBinaryNode> * pVisitor) = 0;

	virtual bool compare(_BaseBinaryNode * pNode) const
	{
		return  compareNodes(this, pNode);
	};

	/*
	 * Get Children Method
	 */
	inline _BaseBinaryNode* getChild(size_t idx) const
	{
		if (idx >= getChildrenCount())
			throw std::runtime_error("::getChild() - incorrect index");

		return m_children[idx];
	};

	/*
	 * Set Children Method
	 */
	inline _BaseBinaryNode* setChild(size_t idx, _BaseBinaryNode* pNode)
	{
		if(idx >= getChildrenCount())
			throw std::runtime_error("setChild() - incorrect index");

		_BaseBinaryNode* pOldNode = m_children[idx];
	    m_children[idx]=pNode;
	    return pOldNode;
	};
};


/*
 * NODE AND
 */
template < NodeType _node_type
		 , class _base_binary = _BaseBinaryNode<true> >
class BinaryNodeImpl
	: public _base_binary
{
public:
	typedef _base_binary BinaryNode;

	explicit BinaryNodeImpl( BinaryNode* lh=NULL,
				    BinaryNode* rh=NULL)
		: _base_binary(_node_type, 2, lh, rh)
	{};

	virtual void accept(NodeVisitor<BinaryNode> * pVisitor)
	{
		pVisitor->visitAnd(this);
	};

	virtual BinaryNodeImpl* clone() const
	{
		return new BinaryNodeImpl(_base_binary::getChild(0)->clone(), _base_binary::getChild(1)->clone());
	};
};


// NODE OR
template <class _base_binary>
class BinaryNodeImpl<NT_OR, _base_binary>
	: public _base_binary
{
public:
	typedef _base_binary BinaryNode;

	explicit BinaryNodeImpl( BinaryNode* lh=NULL,
					    BinaryNode* rh=NULL)
		: _base_binary(NT_OR, 2, lh, rh)
	{};

	virtual void accept(NodeVisitor<BinaryNode> * pVisitor)
	{
		pVisitor->visitOr(this);
	};

	virtual BinaryNodeImpl* clone() const
	{
		return new BinaryNodeImpl(_base_binary::getChild(0)->clone(), _base_binary::getChild(1)->clone());
	};
};


// NODE NOT
template <class _base_binary>
class BinaryNodeImpl<NT_NOT, _base_binary>
	: public _base_binary
{
public:
	typedef _base_binary BinaryNode;

	explicit BinaryNodeImpl(BinaryNode* lh=NULL)
		: _base_binary(NT_NOT, 1, lh)
	{};

	virtual void accept(NodeVisitor<BinaryNode> * pVisitor)
	{
		pVisitor->visitNot(this);
	}

	virtual BinaryNodeImpl* clone() const
	{
		return new BinaryNodeImpl(_base_binary::getChild(0)->clone());
	};
};

// NODE ATOM
template <class _base_binary>
class BinaryNodeImpl<NT_ATOM, _base_binary>
	: public _base_binary
{
	std::string m_text;

public:
	typedef _base_binary BinaryNode;

	explicit BinaryNodeImpl(const std::string & sText)
		: BinaryNode(NT_ATOM, 0)
		, m_text(sText)
	{};
	virtual ~BinaryNodeImpl(){};

	inline std::string getAtomText() const
	{
		return m_text;
	};

	virtual BinaryNode* clone() const
	{
		return new BinaryNodeImpl(m_text);
	};

    virtual void accept(NodeVisitor<BinaryNode> * pVisitor)
    {
        pVisitor->visitAtom(this);
    }

	virtual bool compare(BinaryNode * pNode) const
	{
		if(NT_ATOM != pNode->getNodeType())
			return false;

		return (m_text == static_cast<BinaryNodeImpl<NT_ATOM, _base_binary>*>(pNode)->getAtomText());
	}
};


/**
 *
 */
template<bool _t>
struct NodeTraits<_BaseBinaryNode<_t> >
{
	typedef _BaseBinaryNode<_t>								Node;
	typedef BinaryNodeImpl<NT_AND,	_BaseBinaryNode<_t> >	NodeAnd;
	typedef BinaryNodeImpl<NT_OR,	_BaseBinaryNode<_t> >	NodeOr;
	typedef BinaryNodeImpl<NT_NOT,	_BaseBinaryNode<_t> >	NodeNot;
	typedef BinaryNodeImpl<NT_ATOM,	_BaseBinaryNode<_t> >	NodeAtom;
};

template<bool _t>
struct NodeFactory<_BaseBinaryNode<_t> >
{
	typedef typename NodeTraits<_BaseBinaryNode<_t> >::Node Node;
	typedef typename NodeTraits<_BaseBinaryNode<_t> >::NodeAnd NodeAnd;
	typedef typename NodeTraits<_BaseBinaryNode<_t> >::NodeOr NodeOr;
	typedef typename NodeTraits<_BaseBinaryNode<_t> >::NodeNot NodeNot;
	typedef typename NodeTraits<_BaseBinaryNode<_t> >::NodeAtom NodeAtom;

	inline NodeOr * createNodeOr(Node * leftNode, Node * rightNode) const
	{
		return new NodeOr(leftNode, rightNode);
	};

    inline NodeAnd * createNodeAnd(Node * leftNode, Node * rightNode) const
    {
    	return new NodeAnd(leftNode, rightNode);
    };

    inline NodeNot * createNodeNot(Node * node) const
    {
    	return new NodeNot(node);
    };

    inline NodeAtom * createNodeAtom(const std::string & text) const
    {
    	return new NodeAtom(text);
    };

    inline std::string getFactoryName() const
    {
        return std::string("BinaryNodeFactory");
    };
};

